// ==UserScript==
// @name Better NXU
// @namespace https://thisish.com/
// @version 0.1.0
// @description 这是一个增强 NXU 网站使用体验的JavaScript脚本.
// @author H
// @run-at document-start
// @storageName h.nxu
// @match *://webvpn.nxu.edu.cn/*
// @match *://jsfzyjxzlxt.nxu.edu.cn/*
// @grant unsafeWindow
// @grant noframes
// @grant GM_log
// @grant CAT_userConfig
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addElement
// @grant GM_setClipboard
// @require https://scriptcat.org/lib/1405/^1.0.5/h.notification.js
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.7.0/jquery.min.js
// @require https://unpkg.com/tesseract.js@4.1.4/dist/tesseract.min.js
// @resource svg-logo https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.1/css/all.min.css
// @resource vant-css https://unpkg.com/vant@4/lib/index.css
// @resource vue-js https://unpkg.com/vue@3/dist/vue.global.js
// @resource vant-js https://unpkg.com/vant@4.8.0/lib/vant.min.js
// ==/UserScript==
/* ==UserConfig==
WebVPN:
username:
title: 账号
description: 连接校园网的账号
type: text
default: null
password:
title: 密码
description: 连接校园网的密码
type: text
default: null
password: true
autoLogin:
title: 自动登录WebVPN
description: 是否自动登录WebVPN
type: checkbox
default: false
autoClose:
title: 自动关闭错误网站
description: 是否自动关闭显示错误的网站
type: checkbox
default: false
courseBeautify:
title: 课表美化
description: 是否自动美化课表
type: checkbox
default: false
customCard:
title: 在主页需要添加的卡片
description: 这里可以选择在主页增加的自定义卡片
type: mult-select
default: ['中国知网']
values: ['中国知网', '万方数据']
qualityJson:
title: 评教系统自定义配置
description: 这里可以配置评教系统的自定义设置,请严格按照以下要求:1. 每一条规则均用[]表示,每条规则之间用英文逗号,隔开。2. 内有三个参数,每个参数之间用英文逗号,隔开。3. 参数1为一个数字,表示第几列;参数2为一个字符串,需用英文单引号'引用,表示这一列匹配的内容是什么;参数3为一个数字,1表示完全同意,2表示同意,以此类推。 示例:[[0, '“四史”教育', 2], [1, 'XX学院', 1]]
type: textarea
default: []
==/UserConfig== */
(function() {
'use strict';
unsafeWindow.GM_getResourceText = GM_getResourceText;
//==SomeFunctions==
function getCurrentLineNumber(offset = 2) {
try {
throw new Error();
} catch (e) {
// 获取堆栈信息
const stackLines = e.stack.split('\n');
// 假设第三行包含了当前代码的信息
// 可能需要根据具体情况调整行数偏移量
// myConsole(stackLines)
const line = stackLines[offset].trim();
// // 从行信息中提取行数
const functionName = line.match(/at\s+([a-zA-Z]+)\s+\(/)[1];
const lineNumber = line.match(/:(\d+):(\d+)/)[1];
return functionName+':'+lineNumber;
}
}
function getQuery(msg) {
// 获取当前页面的 URL
let urlString = window.location.href;
// 创建 URL 对象
let url = new URL(urlString);
// 获取查询参数
let searchParams = new URLSearchParams(url.search);
let result = searchParams.get(msg);
return result;
}
//随机数
function random(min, max){
return parseInt(Math.random()*(max-min+1)+min,10);
}
//等待执行
function justWait(min, max = 0, log = true){
var waitmsg,waittime,line;
if (max == 0) {
waittime = min;
waitmsg = `
====================
${getCurrentLineNumber(3)}
等待了:${(waittime / 1000)} 秒
====================
`;
} else {
waittime = random(min, max);
waitmsg = `
====================
${getCurrentLineNumber(3)}
随机等待了:${(waittime / 1000)} 秒
====================
`;
}
return new Promise(function (resolve, reject) {
setTimeout(function () {
if (log) {
myConsole(waitmsg.replace(/ /g,""));
}
resolve();
}, waittime);
});
}
// 自定义输出
function myConsole(msg) {
if (typeof(msg) != 'object') {
if (/\n/.test(msg)) {
console.log("======== Better NXU ========\n" + msg + "\n======== Better NXU ========");
} else {
console.log(
'%c %s %c %s',
'border-radius: 5px;padding: 3px 4px;color: white;background-color: #3a8bff;margin-bottom: 0.5em',
'Better NXU',
'margin-left: 0.6em;font-size:1.2em',
'\n' + msg,
);
}
} else {
console.log(
'%c %s %c %s',
'border-radius: 5px;padding: 3px 4px;color: white;background-color: #3a8bff;margin-bottom: 0.5em',
'Better NXU',
'margin-left: 0.6em;',
'\n下面是一个object对象',
);
console.log(msg);
}
}
//
function jwglClass(content_array, mode = -1) {
if (mode == 0) {
return `
${content_array[3]}
${content_array[1]}
${content_array[2]}
${content_array[0]}
`;} else if (mode > 0) {
return `
${content_array[2]}
${content_array[0]}
`;} else {
return `
${content_array[3]}
${content_array[0]}
${content_array[2]}
${content_array[1]}
`;}
};
//==/SomeFunctions==
//==SomeConstants==
const Url = window.location.href;
const Host = window.location.host;
const Path = window.location.pathname;
const Version = '0.1.0';
const VersionContent = '2023120600';
const NeedSet = '2023120600';
const loadMessage = {"loading tesseract core":"核心加载","initializing tesseract":"初始化","loading language traineddata":"加载语言训练数据","initializing api":"初始化接口","recognizing text":"识别验证码"};
const Start = {
now: function(func) {
eval(func);
window.addEventListener('load', function() {
unsafeWindow.eval(GM_getResourceText("vue-js"));
// unsafeWindow.Vue = Vue;
unsafeWindow.eval(GM_getResourceText("vant-js"));
// unsafeWindow.vant = vant;
});
},
waitDom: function(func) {
document.addEventListener("DOMContentLoaded", function(event) {
eval(func);
window.addEventListener('load', function() {
unsafeWindow.eval(GM_getResourceText("vue-js"));
// unsafeWindow.Vue = Vue;
unsafeWindow.eval(GM_getResourceText("vant-js"));
// unsafeWindow.vant = vant;
});
});
},
waitAll: function(func) {
window.addEventListener('load', function() {
console.log("abc");
unsafeWindow.eval(GM_getResourceText("vue-js"));
// unsafeWindow.Vue = Vue;
unsafeWindow.eval(GM_getResourceText("vant-js"));
// unsafeWindow.vant = vant;
console.log("abc");
eval(func);
});
}
};
//==/SomeConstants==
//==SomeVariables==
//==/SomeVariables==
//==Pretreatment==
//GM_addElement('script', {src: 'https://unpkg.com/vant@4.8.0/lib/vant.min.js', type:'text/javascript'})
GM_addStyle(GM_getResourceText("vant-css"));
// unsafeWindow.vant = vant
GM_addStyle(ToastCss);
GM_addStyle(GM_getResourceText("svg-logo").replace(/\.\.\/webfonts/g, "https://cdn.bootcdn.net/ajax/libs/font-awesome/6.2.1/webfonts"));
unsafeWindow.createToast = createToast;
unsafeWindow.removeToast = removeToast;
unsafeWindow.CAT_userConfig = CAT_userConfig;
unsafeWindow.GM_getResourceText = GM_getResourceText;
//==/Pretreatment==
//开始
myConsole(`开始运行`);
myConsole(`判断页面...`);
myConsole(Url);
switch(Host){
case 'webvpn.nxu.edu.cn':
myConsole("欢迎使用 webvpn");
if (Url.indexOf("service=https%3A%2F%2Fwebvpn.nxu.edu.cn%2Flogin%3Fcas_login%3Dtrue") != -1) {
myConsole("这里是 - 登录页");
Start.waitAll('webvpnLogin()');
} else if (Url == 'https://webvpn.nxu.edu.cn/') {
myConsole("这里是 - 主页");
Start.waitAll('webvpnMain()');
} else if (Url.indexOf('/77726476706e69737468656265737421fae04690693e7045300d8db9d6562d/') != -1) {
myConsole("这里是 - 教务系统");
if (Url.indexOf('cas.action') != -1) {
myConsole("正在 - 主页");
Start.waitAll('webvpnJwglMain()');
} else if (Url.indexOf('courseTableForStd.action') != -1 && getQuery('method') == 'stdHome') {
myConsole("正在 - 课表");
Start.waitDom('webvpnJwglCourseIframe()');
} else if (Url.indexOf('courseTableForStd.action') != -1 && getQuery('method') == 'courseTable') {
myConsole("正在 - 课表");
Start.waitAll('webvpnJwglCourse()');
}
} else if (Url.indexOf('/77726476706e69737468656265737421fbf952d2243e635930068cb8') != -1 || Url.indexOf('/77726476706e69737468656265737421e7e056d2243e635930068cb8') != -1) {
// 77726476706e69737468656265737421e7e056d2243e635930068cb8
// 77726476706e69737468656265737421fbf952d2243e635930068cb8
myConsole("这里是 - 中国知网");
if (Url.indexOf('/KXReader/Detail') != -1) {
myConsole("正在 - html阅读");
Start.waitAll('webvpnCnkiHtml()');
}
}
break;
case 'jsfzyjxzlxt.nxu.edu.cn':
myConsole("欢迎使用评教系统");
if (Path == "/quality/student/evaluate/item_tasks") {
myConsole("这里是 - 选择页");
Start.waitAll('qualityChoose()');
} else if (Path == "/quality/student/evaluate/item_tasks_text") {
myConsole("这里是 - 填写页");
Start.waitAll('qualityText()');
}
break;
case 'jwgl.nxu.edu.cn':
myConsole("欢迎使用教务系统");
if (Path = '/index.action') {
myConsole("这里是 - 登录页");
Start.waitAll('webvpnJwglLogin()');
}
default:
return;
}
return;
async function webvpnLogin() {
const autoLogin = GM_getValue("WebVPN.autoLogin");
if (!autoLogin) {
return;
}
createToast("info", `自动登录...`);
const username = GM_getValue("WebVPN.username", false);
const password = GM_getValue("WebVPN.password", false);
if (!username || !password) {
createToast("error", `
账号密码未配置
请前往配置相关信息
> 前往配置 <
`);
return;
}
if ($('span#msg.auth_error').eq(0).text() == '您提供的用户名或者密码有误') {
createToast("error", `
账号密码配置错误
请前往配置相关信息
> 前往配置 <
`);
return;
}
if ($("p#cpatchaDiv").html().replace(/\s/g, '') != '') {
createToast("warning", `请手动输入验证码登录`, 0);
return;
}
$('input#username').attr('value', username);
$('input#password').attr('value', password);
$('button[type=submit]').click();
}
async function webvpnMain() {
const firstSet = GM_getValue('firstSet');
const needSet = GM_getValue('needSet');
// createToast("success", "测试消息", 0);
//更新配置弹窗
GM_addElement(document.querySelector('body'), 'div', {id:'update'});
var update_template = '', update_show = false;
if (!firstSet) {
update_template = `
这好像是你第一次使用本插件
我们需要一些配置信息
你可以选择前往配置
或点击取消不进行配置
后续自行前往设置页面进行配置
`;
update_show = true;
} else if (needSet != NeedSet) {
update_template = `
V ${Version}
我们更新了一些配置信息
你可以选择前往配置
或点击取消不进行配置
后续自行前往设置页面进行配置
`;
update_show = true;
}
const update = Vue.createApp({
template: update_template,
setup() {
const show = Vue.ref(false);
show.value = update_show;
const goConfig = () => {
CAT_userConfig();
}
const closeFunc = () => {
GM_setValue('firstSet', true);
GM_setValue('needSet', NeedSet);
}
return { show, goConfig, closeFunc };
}
});
update.use(vant);
update.mount("#update");
//更新弹窗
const version = GM_getValue('version');
if (version != Version) {
GM_setValue('version', Version);
createToast("info", `
Better NXU V ${Version} 已更新!
${VersionContent}
> 前往配置 <
`);
}
//自定义卡片
const webVPNCustomCard = GM_getValue("WebVPN.customCard");
$('.portal-content__block .el-scrollbar__view').eq(0).prepend(`
`);
$('div[data-id=classes] div.block-group__content').eq(0).append(`
`);
if (webVPNCustomCard.length != 0) {
$('.portal-content__block .el-scrollbar__view').eq(0).prepend(`
`);
if (webVPNCustomCard.indexOf('中国知网') != -1) {
$('div[data-id=custom] div.block-group__content').eq(0).append(`
`);
}
if (webVPNCustomCard.indexOf('万方数据') != -1) {
$('div[data-id=custom] div.block-group__content').eq(0).append(`
`);
}
}
}
async function webvpnCnkiHtml() {
function getCurrentSelect(){
let selectionObj = null, rangeObj = null;
let selectedText = "", selectedHtml = "";
// 处理兼容性
if(window.getSelection){
// 现代浏览器
// 获取text
selectionObj = window.getSelection();
selectedText = selectionObj.toString();
// 获取html
rangeObj = selectionObj.getRangeAt(0);
var docFragment = rangeObj.cloneContents();
var tempDiv = document.createElement("div");
tempDiv.appendChild(docFragment);
selectedHtml = tempDiv.innerHTML;
} else if(document.selection){
// 非主流浏览器IE
selectionObj = document.selection;
rangeObj = selectionObj.createRange();
selectedText = rangeObj.text;
selectedHtml = rangeObj.htmlText;
}
return {
text: selectedText,
html: selectedHtml
}
};
// 使标题可选中
$('h1#topTitle.title').attr('style', 'user-select:auto;');
myConsole("复制已开启");
// 监听内容区域鼠标抬起事件
document.addEventListener('mouseup', function(){
var copy = getCurrentSelect();
if (copy.text == "") {
return;
}
// myConsole('onmouseup');
myConsole(getCurrentSelect());
GM_setClipboard(getCurrentSelect().text);
});
}
async function webvpnJwglMain() {
GM_addElement(document.querySelector('body'), 'div', {id:'course-beautify'});
await justWait(1000);
const course_beautify = Vue.createApp({
template: `
`,
setup() {
const onClick = () => {
vant.showToast('尚未实现的功能');
}
return { onClick };
}
});
course_beautify.use(vant);
course_beautify.mount("#course-beautify");
}
async function webvpnJwglCourseIframe() {
// 获取 iframe 元素
var iframe = document.querySelector("#contentListFrame");
// 等待 iframe 中的内容加载完成后获取内容高度并设置 iframe 高度
iframe.onload = function() {
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
var height = iframeDocument.body.scrollHeight + 'px';
iframe.style.height = height;
};
}
async function webvpnJwglCourse() {
if (!GM_getValue('WebVPN.courseBeautify')) {
return;
}
$("body").html($("body").html().replace(".noneprint{\n\tdisplay:none\n} \n\n",""))
$("table.listTable#contentListFrame").addClass('optimized');
$("table").css("margin-bottom", "3em");
$("tr").attr("height", "auto");
$("tr").css("min-height", "45px");
$("td").css("padding-left", "0");
$("td > div").each(function() {
var div = $(this);
//div.html(div.attr('title'));
div.css("height", "auto");
div.css("padding", "1em 0.5em 0");
var content = div.attr('title');
// content = content.split(/(? 1) {
if (i == 0 || content_array[i][1] == content_array[i-1][1]) {
div.append(jwglClass(content_array[i], i));
} else {
div.append('')
div.append(jwglClass(content_array[i], 0));
}
} else {
div.append(jwglClass(content_array[i]));
}
}
});
}
async function qualityChoose() {
console.log(111)
unsafeWindow.$ = $;
// $(".layui-table tr").each(function() {
// var tr = $(this);
// var choose = 1;
// if (tr.find('td').eq(1).text() == '教学运行保障部') {
// choose = 2;
// }
// if (tr.find('td').eq(0).text() == '“四史”教育') {
// choose = 2;
// }
// if (tr.find('td').eq(3).find('input').length != 0) {
// tr.find('td').eq(3).find('div > div').eq(choose).click();
// }
// $('.layui-btn').click();
// });
// createToast("info", "正在自动评教...", 0);
var tr = document.querySelectorAll('.layui-table tr');
for (let i = 0; i < tr.length; i++) {
var choose = 0;
if (tr[i].querySelectorAll('td')[1].innerHTML == '教学运行保障部') {
choose = 2;
}
if (tr[i].querySelectorAll('td')[0].innerHTML == '“四史”教育') {
choose = 2;
}
if (tr[i].querySelectorAll('td')[3].querySelectorAll('div > div').length != 0) {
tr[i].querySelectorAll('td')[3].querySelectorAll('div > div')[choose].click();
}
await justWait(100);
}
$('.layui-btn').click();
}
async function qualityText() {
const teacherEvaluations = [
"这位老师讲解清晰,深入浅出,使复杂的内容变得易于理解。",
"教学风格独特,注重互动,让课堂生动有趣。",
"激发学生学习兴趣,引导他们主动参与讨论与思考。",
"善于运用多媒体资源,提升教学效果。",
"对学生关心备至,经常与他们交流,关心他们的学业和生活。",
"在解决问题时耐心细致,乐于帮助学生克服困难。",
"组织能力强,课堂安排合理,确保学生充分掌握知识。",
"注重培养学生的批判性思维和创新能力。",
"鼓励学生勇于表达意见,营造积极向上的学习氛围。",
"对待学生公正公平,让每个学生都感受到被尊重和重视。",
"这位老师充满热情,激励学生探索知识的乐趣。"
];
// createToast("info", "正在自动评教...", 0);
var tr = document.querySelectorAll('.layui-table tr');
for (let i = 0; i < tr.length; i++) {
if (tr[i].querySelectorAll('td')[3].querySelectorAll('textarea').length != 0) {
tr[i].querySelectorAll('td')[3].querySelectorAll('textarea')[0].value = teacherEvaluations[random(0, teacherEvaluations.length - 1)];
}
await justWait(100);
}
$('.layui-btn').click();
}
})();